#include "glwidget.h"

GLWidget::GLWidget(MyCallback *myCallback_, QWidget *parent) :
    QGLWidget(parent), timer(new QBasicTimer), myCallback(myCallback_)
{
     painter = new QPainter();
    setAutoBufferSwap(false);

    pathGenerated = 0;
    qsrand(QDateTime::currentMSecsSinceEpoch());

    totalFingerMovementX = 0;
    totalFingerMovementY = 0;
    lastFingerX = 0;
    lastFingerY = 0;
    inPathOnset = 0;
    inPathOffset = 0;
    inPathFinished = 0;
    inPathTrial = 0;
    fps = 0;
    framesCollected = -1;
    startFpsTime = 0;
    pathYLength = 0;
    pathYBegin = 0;
    requestedFps = 50;

    noSegments = 20;
    yDelta = 0;

    lastPointIndex = 0;

    trialDuration = 1000 * 60 * 10;

    screenNumber = 1;




    fingerX = fingerY = 0;
    fingerRadius = 60;
    finger = QRect(fingerX,fingerY,fingerRadius,fingerRadius);

    targetDistance = 0;
    errorScore = 0;

    pause = 0;

    fingerPositionVec = new QVector<int>(trialDuration/1000*requestedFps*2);
    pathPositionVec = new QVector<int>(trialDuration/1000*requestedFps*2);

    frame = 0;



}

void GLWidget::setUI()
{
    tempX = this->w/2;
    fingerAreaRect=QRect(0,this->h-300,this->w,200);
    fingerY = this->h/2;
    fingerX = this->w/2 - fingerRadius/2;
    finger.setRect(fingerX,fingerY,fingerRadius,fingerRadius);

    quitButtonRect = QRect(this->w-150,50,100,100);


}

void GLWidget::generatePath()
{

    if (pathGenerated)
        return;

    setDifficulty();



    pathGenerated = 1;
    path.moveTo(this->w/2,0);

    int requiredPathLength = trialDuration * requestedFps / 1000.0 * speed;


    //onset line
    path.lineTo(this->w/2, this->h);

    double meanLength = 0;

    pathYBegin = path.currentPosition().y();

    //continue building path until we have enough length
    int i=0;
    while(1)
    {

        path.lineTo(this->w/2 + (0.5-qrand()/(double)RAND_MAX)*2*baseXDelta,qrand()/(double)RAND_MAX*baseYDelta+minYDelta+path.currentPosition().y());


        pathYLength = path.currentPosition().y() - pathYBegin;
        meanLength = pathYLength / (i+1);
        ++i;

        if ((requiredPathLength - meanLength) <= pathYLength)
            break;

    }

    path.lineTo(this->w/2 ,qrand()/(double)RAND_MAX*baseYDelta+minYDelta+path.currentPosition().y());
    pathYLength = path.currentPosition().y() - pathYBegin;


    //offset line
    path.lineTo(this->w/2, path.currentPosition().y() + this->h/2);

    path.setFillRule(Qt::WindingFill);
    for (int i=0; i<path.elementCount(); ++i)
    {
        QString e = QString::number(path.elementAt(i).x) + ";"+QString::number(path.elementAt(i).y)+"@";
        pathString += e;
    }


}



void GLWidget::setDifficulty()
{

    this->minYDelta = 50;

    if (difficultyLevel <= 7)
    {
        this->speed = 2;
    }
    else if (difficultyLevel <= 14)
    {
        this->speed = 3;
    }
    else
    {
        this->speed = 4;
    }

    double wM = 1;
    if (difficultyLevel >= 1 && difficultyLevel <= 4)
        wM = 1;
    if (difficultyLevel >= 5 && difficultyLevel <= 8)
        wM = 1.1;
    if (difficultyLevel >= 9 && difficultyLevel <= 12)
        wM = 1.2;
    if (difficultyLevel >= 13 && difficultyLevel <= 16)
        wM = 1.3;
    if (difficultyLevel >= 17 && difficultyLevel <= 20)
        wM = 1.4;
    if (difficultyLevel >= 21)
        wM = 1.5;

    this->baseYDelta = 200 - 7 * difficultyLevel;
    this->baseXDelta = this->w/4 * wM;



}


void GLWidget::resizeGL(int w, int h)
{
    this->w = w;
    this->h = h;


    generatePath();
    setUI();
    aspectRatio = (qreal) w / (qreal) h;

}

void GLWidget::initializeGL()
{
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    timer->start(1/(double)requestedFps * 1000.0, this);
}



void GLWidget::paintGL()
{


    painter->begin(this);

    painter->fillRect(this->rect(),QBrush(QColor(255,255,255)));
    painter->fillRect(fingerAreaRect,QBrush(QColor("gray")));

    painter->setPen(QPen(QColor("black"), 4));
    painter->drawPath(path);

    painter->setPen(QPen(QColor("red"), 3));
    painter->drawEllipse(finger);
    painter->drawPoint(tempX,this->h/2+fingerRadius/2);

/*
    painter->drawText(20,100,fpsString);
    painter->drawText(20,200,QString("left:")+QString::number((int)(trialProgress*trialDuration/1000.0))+QString("s"));
    painter->drawText(20,300,QString("error:")+QString::number(errorViz,'f',2));
*/

    painter->end();
    swapBuffers();


}

void GLWidget::timerEvent(QTimerEvent *e)
{


    int previousX = 0;
    int previousY = 0;
    for (int i = lastPointIndex; i < path.elementCount(); ++i)
    {
        int x = path.elementAt(i).x;
        int y = path.elementAt(i).y;

        if (previousY <= (fingerY + fingerRadius/2) && y >= (fingerY + fingerRadius/2))
        {
            lastPointIndex = i-1;
            if (x == previousX)
                tempX = previousX;
            else
            {
                double m = (y-previousY)/(double)(x-previousX);
                tempX = ((fingerY + fingerRadius/2) - previousY)/m + previousX;
            }

            break;
        }

        previousX = x;
        previousY = y;

    }



    if (path.elementAt(1).y >= (fingerY+fingerRadius/2)) {inPathOnset = 1; inPathOffset = 0; inPathFinished = 0; inPathTrial = 0;}
    else if (path.elementAt(path.elementCount()-1).y <= (fingerY+fingerRadius/2)) {inPathOnset = 0; inPathOffset = 0; inPathFinished = 1; inPathTrial = 0;}
    else if (path.elementAt(path.elementCount()-2).y <= (fingerY+fingerRadius/2)) {inPathOnset = 0; inPathOffset = 1; inPathFinished = 0; inPathTrial = 0;}
    else {inPathOnset = 0; inPathOffset = 0; inPathFinished = 0; inPathTrial = 1;}

    targetDistance = abs(fingerX - tempX + fingerRadius/2);

    if (inPathOnset)
        trialProgress = 1;
    if (inPathTrial)
        trialProgress =  (path.elementAt(path.elementCount()-3).y - this->h/2)/double(pathYLength);
    if (trialProgress < 0) trialProgress = 0;
    if (trialProgress > 1) trialProgress = 1;




    if (inPathFinished)
    {

       //qDebug() <<  tempString;
        timer->stop();
        saveData();

    }

    if (inPathTrial)
    {
        int currentError = targetDistance;
        if (targetDistance <= fingerRadius/2) currentError = 0;
        else currentError -= fingerRadius/2;
        errorScore += currentError/100.0;

        (*fingerPositionVec)[frame] = fingerX;
        (*pathPositionVec)[frame] = tempX;

                if (frame%(10*requestedFps) == 0)
                  myCallback->insertIntoMetaFile("frame_"+QString::number(frame));

        ++frame;
    }

    if (framesCollected==-1) startFpsTime = QDateTime::currentMSecsSinceEpoch();
    ++framesCollected;



    if (framesCollected == 200)
    {
        fps = framesCollected / long(QDateTime::currentMSecsSinceEpoch() - startFpsTime) * 1000.0;
        startFpsTime = QDateTime::currentMSecsSinceEpoch();
        framesCollected = 0;
        errorViz = errorScore;
	fpsString = QString("fps:")+QString::number(fps);
    }


    path.translate(0,-speed);
    //paintGL();
    updateGL();

}

void GLWidget::mousePressEvent(QMouseEvent* event)
{


    if (quitButtonRect.contains(event->pos()))
        QApplication::quit();

    //pause = 1;
    lastFingerX = event->pos().x();
    lastFingerY = event->pos().y();
    fingerX = event->pos().x() - fingerRadius/2;
    finger.setRect(fingerX,fingerY,fingerRadius,fingerRadius);
}

void GLWidget::mouseMoveEvent(QMouseEvent* event)
{
    if(!(event->buttons() & Qt::LeftButton))
        return;

    if(dragging)
    {
        fingerX = event->pos().x() - fingerRadius/2;
        finger.setRect(fingerX,fingerY,fingerRadius,fingerRadius);

        totalFingerMovementX += abs(event->pos().x() - lastFingerX);
        totalFingerMovementY += abs(event->pos().y() - lastFingerY);

        lastFingerX = event->pos().x();
        lastFingerY = event->pos().y();

        //pause = 1;
    }

    dragging = true;

}

void GLWidget::mouseReleaseEvent(QMouseEvent* event)
{
    dragging = false;
    pause = 0;
}

void GLWidget::quit()
{
    timer->stop();
}

void GLWidget::saveData()
{

    QString masterString;



    masterString.append(QString(QString("timestamp:")+QString::number(QDateTime::currentMSecsSinceEpoch()) + "\n"));
    masterString.append(QString(QString("username:")+username + "\n").toStdString().c_str());
    masterString.append(QString(QString("desc:")+description + "\n").toStdString().c_str());
    masterString.append(QString(QString("difficulty:")+QString::number(difficultyLevel) + "\n").toStdString().c_str());
    masterString.append(QString(QString("error:")+QString::number(errorScore,'f',2) + "\n").toStdString().c_str());
    masterString.append(QString("path:").toStdString().c_str());
    masterString.append(pathString.toStdString().c_str());
    masterString.append("\n");

    masterString.append(QString(QString("requestedFPS:")+QString::number(requestedFps)+"\n").toStdString().c_str());

    masterString.append(QString("movements:").toStdString().c_str());
    QString movementsString;
    for (int i=0; i<frame; ++i)
    {
        movementsString += (QString(QString::number(i)+";"+QString::number(fingerPositionVec->at(i)+fingerRadius/2)+";"+QString::number(pathPositionVec->at(i))+"@").toStdString().c_str());
    }

    masterString.append(movementsString);
    masterString.append("\n");

    QFile* motorFile = new QFile(myCallback->getRawFilename()+".motor");
    if (!motorFile->open(QIODevice::WriteOnly | QIODevice::Text))
    {
        qDebug() << "problem creating motor file";
        return;
    }

    motorFile->write(masterString.toStdString().c_str());
    motorFile->close();
    delete motorFile;

    QFile* lastFile = new QFile(Sbs2Common::getCatalogPath()+"lastSession");
    if (!lastFile->open(QIODevice::WriteOnly | QIODevice::Text))
    {
        qDebug() << "problem creating last session file";
        return;
    }

    lastFile->write(QString(QString("username:")+username + "\n").toStdString().c_str());
    lastFile->write(QString(QString("lastFile:")+myCallback->getRawFilename()+".motor").toStdString().c_str());
    lastFile->close();
    delete lastFile;


    emit finishGame();

    /*
    gzFile o = gzopen(QString(myCallback->getRawFilename()+".motor").toStdString().c_str(),"w");
    QString data = masterString;
    gzputs(o, data.toStdString().c_str());
    gzclose(o);
*/


}

void GLWidget::setUser(QString username_, QString description_)
{
    username = username_;
    description = description_;
}

void GLWidget::setDifficultyLevel(int level)
{
    difficultyLevel = level;
}

double GLWidget::getError()
{
    return errorScore;
}
